package io.renren.modules.yckj.common.service;

import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.cipher.RSASigner;
import com.wechat.pay.java.core.cipher.Signer;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.util.IOUtil;
import com.wechat.pay.java.core.util.PemUtil;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import io.renren.modules.yckj.common.wx.WeChatProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * JsapiService使用示例
 */
@Component
public class JsapiPayService {

    @Autowired
    private WeChatProperties weChatProperties;

    //    @PostConstruct
    public JsapiService getService() {
        // 初始化商户配置
        Config config =
                new RSAAutoCertificateConfig.Builder()
                        .merchantId(weChatProperties.getMerchantId())
                        // 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
                        .privateKeyFromPath(weChatProperties.getPrivateKeyPath())
                        .merchantSerialNumber(weChatProperties.getMerchantSerialNumber())
                        .apiV3Key(weChatProperties.getApiV3Key())
                        .build();

        // 初始化服务
        return new JsapiService.Builder().config(config).build();
    }

    /**
     * @param openid      wx openid
     * @param id          业务id
     * @param total       金额
     * @param attach      扩展信息
     * @param description 描述
     * @return 待支付签名
     */
    public Map<String, Object> sign(String openid, Long id, Integer total, String attach, String description) {
        // ... 调用接口
        PrepayResponse prepay = prepay(openid, id, total, attach, description);
        /**
         * 下面为使用加密获取密钥,也需要返回给前段
         */
        long timeStamp = System.currentTimeMillis() / 1000;
        String nonceStr = UUID.randomUUID().toString().replace("-", "");
        //构造签名参数(appid更换为实际支付人员的appid)
        String prepay_id = prepay.getPrepayId();
        String message = weChatProperties.getAppid() + "\n" + timeStamp + "\n" + nonceStr + "\n" + "prepay_id=" + prepay_id + "\n";
        //生成签名
        try {
            String keyString = IOUtil.loadStringFromPath(weChatProperties.getPrivateKeyPath());
            PrivateKey privateKey = PemUtil.loadPrivateKeyFromString(keyString);
            Signer rsaSigner = new RSASigner(weChatProperties.getMerchantSerialNumber(), privateKey);
            String sign = rsaSigner.sign(message).getSign();
            //前段通过该签名可以调用支付
//            System.out.println("sign==>" + sign);
//            System.out.println("message==>" + message);
            /**
             * 注意:
             * 1.预支付订单号(prepay_id),以及生成签名的timeStamp,nonceStr的参数都需要返回前端
             * 2.在构造签名参数的时候,\n一定要加 否则也是签名无效
             * 3.
             */
            Map<String, Object> params = new HashMap<>();
            params.put("paySign", sign);
            params.put("package", "prepay_id=" + prepay_id);
            params.put("nonceStr", nonceStr);
            params.put("timeStamp", "" + timeStamp);
            return params;
        } catch (IOException e) {
//            System.out.println("ld:e" + e.getMessage());
            throw new RuntimeException(e);
        }
    }

    /**
     * 关闭订单
     */
    public void closeOrder() {
        CloseOrderRequest request = new CloseOrderRequest();
        // 调用request.setXxx(val)设置所需参数，具体参数可见Request定义
        // 调用接口
        getService().closeOrder(request);
    }

    /**
     * JSAPI支付下单
     */
    public PrepayResponse prepay(String openid, Long id, Integer total, String attach, String description) {
        PrepayRequest request = new PrepayRequest();
        // 调用request.setXxx(val)设置所需参数，具体参数可见Request定义
        Amount amount = new Amount();
        //设置金额, 以分为单位 金额根据实际情况自行填写
        amount.setTotal(total);
        amount.setCurrency("CNY");
        request.setAmount(amount);
        //绑定微信支 公众号的appid 根据实际情况自行获取填写
        request.setAppid(weChatProperties.getAppid());
        // 商户系统内部订单号，只能是数字、大小写字母_-*且在同一个商户号下唯一,根据自己需求自己定义
        request.setMchid(weChatProperties.getMerchantId());
        request.setDescription(description);
        //支付成功后回调的URL
        request.setNotifyUrl(weChatProperties.getNotifyUrl());
        //ZHE
        request.setOutTradeNo(id.toString());

        //自定义信息, 在支付订单回调可以取到(自行替换,如不需要可注释掉)
        request.setAttach(attach);
        Payer payer = new Payer();
        //支付人员的的openid

        payer.setOpenid(openid);
        request.setPayer(payer);
        //调用支付接口
        PrepayResponse response = getService().prepay(request);
        //响应体中有预支付订单号
        System.out.println(response.getPrepayId());
        //以上为获取预支付订单号步骤
        return response;
    }

    /**
     * 微信支付订单号查询订单
     */
    public Transaction queryOrderById(String wxOrderId) {
        QueryOrderByIdRequest request = new QueryOrderByIdRequest();
        request.setMchid(weChatProperties.getMerchantId());
        request.setTransactionId(wxOrderId);
        // 调用request.setXxx(val)设置所需参数，具体参数可见Request定义
        // 调用接口
        try {
            Transaction result = getService().queryOrderById(request);
            System.out.println(result.toString());
            return result;
        } catch (ServiceException e) {
            // API返回失败, 例如ORDER_NOT_EXISTS
            System.out.printf("code=[%s], message=[%s]\n", e.getErrorCode(), e.getErrorMessage());
            System.out.printf("reponse body=[%s]\n", e.getResponseBody());
        }
        return null;
    }

    /**
     * 商户订单号查询订单
     */
    public Transaction queryOrderByOutTradeNo(String orderId) {
        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        request.setMchid(weChatProperties.getMerchantId());
        request.setOutTradeNo(orderId);
        // 调用request.setXxx(val)设置所需参数，具体参数可见Request定义
        // 调用接口
        return getService().queryOrderByOutTradeNo(request);
    }


    public String decryptToString(byte[] associatedData, byte[] nonce, String cipherText) throws GeneralSecurityException {
        try {
            String aesKey = weChatProperties.getApiV3Key();
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            SecretKeySpec key = new SecretKeySpec(aesKey.getBytes(StandardCharsets.UTF_8), "AES");
            GCMParameterSpec spec = new GCMParameterSpec(128, nonce);
            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData);
            return new String(cipher.doFinal(Base64.getDecoder().decode(cipherText)), StandardCharsets.UTF_8);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException(e);
        } catch (Exception ignored) {

        }
        return "";
    }
}